#if !defined(__OTA_H__)
#define __OTA_H__

#include <stdio.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "esp_delta_ota.h"

#define WEB_SERVER "maplerian.dynv6.net"
#define WEB_PORT "8471"
#define OTA_APP_BIN_PATH "/app.bin"
#define OTA_STORAGE_BIN_PATH "/www.bin"

#define OTA_STATUS_NULL 0
#define OTA_STATUS_RUNNING 1
#define OTA_STATUS_FAILED_INVALID_ARGS -1
#define OTA_STATUS_FAILED_WRITE_ABORT -2
#define OTA_STATUS_FAILED_VALIDATE -3
#define OTA_STATUS_FAILED_ERR -4
#define OTA_STATUS_FAILED_SET_BOOT_APP -5
#define OTA_STATUS_FAILED_PARTITION_WRITE_BEGIN -6
#define OTA_STATUS_FAILED_PARTITION_WRITE -7
#define OTA_STATUS_FAILED_PARTITION_TABLE_CHECKED -8
#define OTA_STATUS_FAILED_FIND_PARTITION -9
#define OTA_STATUS_FAILED_CALL_ABORT -10

#define OTA_TYPE_APP (1 << 0)
#define OTA_TYPE_WWW (1 << 1)
#define OTA_TYPE_FILE (1 << 7)

typedef struct ota_plugin
{
    const char *const name;
    void (*loop)(void *ota, void *user_data);
    void *user_data;
    struct ota_plugin *next;
} ota_plugin_s, *ota_plugin_t;

typedef struct ota
{
    uint32_t MAGIC_START;
    char cmd_list[5][256];               /* 命令缓冲区 */
    char err_str[384];                   /* 错误消息缓存 */
    char progress_str[5];                /* 更新进度字符串缓存 */
    uint16_t version;                    /* 当前版本信息 */
    uint16_t last_version;               /* 上次更新版本 */
    char next_version[32];               /* OTA版本信息 */
    char url[256];                       /* 网络升级时需要的服务器路径 */
    char par_name[17];                   /* 需要更新的分区名称 */
    const esp_partition_t *par_info;     /* 需要更新的分区信息 */
    esp_ota_handle_t update_handle;      /* 升级APP时需要的句柄 */
    esp_delta_ota_handle_t delta_handle; /* 升级APP时需要的句柄 */
    void *buffer;                        /* 临时缓冲区 */
    uint32_t buffer_offset;              /* 临时缓冲区偏移 */
    uint32_t buffer_size;                /* 临时缓冲区大小 */
    uint8_t type;                        /* 升级类型 */
    int8_t status;                       /* 当前升级状态 */
    uint8_t progress;                    /* 当前进度 */
    uint32_t length;                     /* 当前升级文件大小 */
    uint32_t read_size;                  /* 读取当前升级文件的大小 */
    uint32_t write_size;                 /* 当前升级文件总写入大小 */
    uint32_t offset;                     /* 当固件获取方式支持偏移的情况下, 可以用此变量保存当前写入的偏移位置 */
    TaskHandle_t handle;                 /* ota 线程句柄 */
    uint32_t tick;                       /* 成功升级APP后, 用于延迟复位 */
    uint32_t total_time;                 /* 从开始到结束的总时间 */
    uint8_t cmd_run : 1;                 /* 目录文件 OTA升级 处理 */
    uint8_t run : 1;                     /* ota 线程处理控制 */
    uint8_t checkVersion : 2;            /* 检测版本处理方式: 0: > 当前版本 1:>=当前版本 2:不检测 */
    uint8_t delta_ota : 1;               /* 差分OTA */
    uint8_t abort : 1;                   /* 终止 ota 线程 */
    ota_plugin_t plugins;                /* 插件 */
    uint32_t MAGIC_END;
} ota_s, *ota_t;
typedef uint8_t (*ota_stream_cb_t)(void *handle, uint8_t *data, uint32_t size);
extern ota_t ota_default_handle;

void ota_init(void);
/**
 *  获取当前更新进度(字符串)
 */
char *ota_get_progress(ota_t ota);
/**
 *  获取当前已写入位置
 */
uint32_t ota_get_offset(ota_t ota);
/**
 *  添加插件到运行过程
 *  ota     :   OTA句柄
 *  plugin  :   插件接口
 *  返回:
 *      NULL    :   失败
 *      plugin  :   成功添加的插件
 */
ota_plugin_t ota_add_plugin(ota_t ota, ota_plugin_t plugin);
/**
 *  设置 OTA 其它分区的 分区名
 *  ota     :   OTA句柄
 *  name    :   分区名
 */
void ota_set_par_name(ota_t ota, char *name);
/**
 *  设置网络 OTA更新
 *  ota     :   OTA句柄
 *  server  :   服务器IP地址
 *  port    :   服务器端口
 *  path    :   文件路径
 *  type    :   更新类型(详情查看 OTA_TYPE_XXXXX 宏)
 */
void ota_set_server(ota_t ota, char *server, char *port, char *path, uint8_t type);
/**
 *  设置网络 OTA更新
 *  ota     :   OTA句柄
 *  url     :   URL地址
 *  type    :   更新类型(详情查看 OTA_TYPE_XXXXX 宏)
 */
void ota_set_url(ota_t ota, char *url, uint8_t type);
/**
 *  网络OTA更新(与 ota_default_handle 共用缓冲区, 一般用于没有OTA线程的情况下)
 *  type    :   更新类型(详情查看 OTA_TYPE_XXXXX 宏)
 *  url     :   URL地址
 *  返回:
 *      1   :   成功
 *      0   :   失败
 */
uint8_t ota_http_update_url(uint8_t type, char *url);
/**
 *  文件OTA更新(可执行指定ota缓冲区, 一般用于没有OTA线程的情况下)
 *  ota     :   ota句柄
 */
uint8_t ota_update(ota_t ota);

/* 默认的流接口回调 */
/* app 分区 OTA流接口 */
uint8_t ota_app_bin_stream_process_cb(void *handle, uint8_t *data, uint32_t len);
/* WWW OTA流接口 */
uint8_t ota_www_bin_stream_process_cb(void *handle, uint8_t *data, uint32_t len);
uint8_t http_stream_header_cb(void *handle, char *key, char *value);

#endif // __OTA_H__
